package com.gpj.learn.netty.nio.simple;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @className NIOServer
 * @description TODO
 * @author GPJ
 * @date 2020/5/12 18:08
 * @version 1.0
 **/
public class NioServer {
    private static final long TIME_OUT = 2 * 1000;

    private ServerSocketChannel serverChannel;
    private Selector selector;

    /**
     * 初始化服务端socket，并注册接受连接事件(accept)监听
     * @param port 监听端口号
     */
    public void init(int port) throws IOException {
        // 开启服务端channel
        serverChannel = ServerSocketChannel.open();

        // 开启selector
        selector = Selector.open();

        // 绑定端口
        serverChannel.bind(new InetSocketAddress(port));

        // 服务端channel设置为非阻塞
        serverChannel.configureBlocking(false);

        // 注册accept事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 轮训监听事件
     */
    public void eventPoll() throws IOException {
        // rebuid阈值(用于处理NIO源码bug)
        int rebuidNum = 0;

        // 轮训监听事件触发情况
        while (true){
            long start = System.nanoTime();
            // 轮询阻塞time_out毫秒，检查是否有事件被触发
            selector.select(TIME_OUT);

            // 消除NIO源码bug，即selector.select(TIME_OUT)阻塞失效
            if(System.nanoTime() - start < TIME_OUT * 1000){
                if(rebuidNum < 10){
                    rebuidNum++;
                }else{
                    rebuildSelector();
                    rebuidNum = 0;
                }
            }

            // 获取触发事件对应的selectedKey
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 进行对应业务处理
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if(selectionKey.isValid()){
                    eventHandle(selectionKey);
                    iterator.remove();
                }
            }
        }
    }

    private void eventHandle(SelectionKey selectionKey) throws IOException {
        if(selectionKey.isAcceptable()){// 触发accept事件
            //获取网络通道
            SocketChannel socketChanne = serverChannel.accept();
            //设置非阻塞式
            socketChanne.configureBlocking(false);
            //连接上了  注册读取事件
            socketChanne.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
            // 实际业务处理...
        }else if(selectionKey.isReadable()){// 触发read事件
            // 获取socketChannel
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            // 获取附属对象
            ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
            // IO操作和业务处理...

            // 监听事件变换
            selectionKey.interestOps(SelectionKey.OP_WRITE);
        }else if(selectionKey.isWritable()){// 触发write事件
            // 获取socketChannel
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            // 获取附属对象
            ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
            // IO操作和业务处理...

            // 监听事件变换
            selectionKey.interestOps(SelectionKey.OP_READ);
        }
    }

    private void rebuildSelector(){
        try {
            // 创建新的轮询器(selector)
            final Selector newSelector = Selector.open();
            Selector oldSelector = selector;

            // 将旧轮询器上边的监听，复制到新轮询器上边
            for (SelectionKey key : selector.keys()) {
                key.channel().register(newSelector, key.interestOps());
            }
            selector = newSelector;
            // 清空旧轮询器
            oldSelector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
